home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr49 / strpp300.zip / REGEXP.CPP < prev    next >
C/C++ Source or Header  |  1993-04-11  |  16KB  |  666 lines

  1. /* -------------------------------------------------------------------- */
  2. /* String++ Version 3.00                                       04/10/93 */
  3. /*                                                                      */
  4. /* Enhanced string class for Turbo C++/Borland C++.                     */
  5. /* Copyright 1991-1993 by Carl W. Moreland                              */
  6. /*                                                                      */
  7. /* Derived from code Copyright 1988, Jim Mischel.  All rights reserved. */
  8. /*                                                                      */
  9. /* regexp.cpp                                                           */
  10. /* -------------------------------------------------------------------- */
  11.  
  12. #include <stddef.h>
  13. #include "regexp.h"
  14.  
  15. #define    TRUE        -1
  16. #define    FALSE        0
  17. #define ALMOST        1    // returned when a closure matches a NULL
  18.  
  19. const char ENDSTR    = '\0';
  20. const char EOL        = '$';
  21. const char BOL        = '^';
  22. const char NEGATE    = '^';
  23. const char CCL        = '[';
  24. const char NCCL        = ']';
  25. const char CCLEND    = ']';
  26. const char ANY        = '.';
  27. const char DASH        = '-';
  28. const char OR        = '|';
  29. const char ESCAPE    = '\\';
  30. const char LPAREN    = '(';
  31. const char RPAREN    = ')';
  32. const char CLOSURE    = '*';
  33. const char POS_CLO    = '+';
  34. const char ZERO_ONE    = '?';
  35. const char LITCHAR    = 'c';
  36. const char END_TERM    = 'e';
  37. const String FS_DEFAULT    = "[ \t]+";
  38.  
  39. int    RSTART;            // start of matched substring
  40. int    RLENGTH;            // length of matched substring
  41. int    NF;            // number of fields from most current split
  42. RegExp FS(FS_DEFAULT);        // global field separator
  43.  
  44. /* -------------------------------------------------------------------- */
  45.  
  46. RegExp::RegExp(const String& s)
  47. {
  48.   reExpression = s;
  49.  
  50.   if(reExpression() != "")
  51.     MakePattern();
  52. }
  53.  
  54. void RegExp::operator=(const char* p)
  55. {
  56.   reExpression = p;
  57.  
  58.   if(reExpression() != "")
  59.     MakePattern();
  60. }
  61.  
  62. void RegExp::operator=(const String& s)
  63. {
  64.   reExpression = s;
  65.  
  66.   if(reExpression() != "")
  67.     MakePattern();
  68. }
  69.  
  70. /* -------------------------------------------------------------------- */
  71.  
  72. // MakePattern() - "compile" the regular expression into rePattern
  73.  
  74. const char* RegExp::MakePattern(void)
  75. {
  76.   static String tmp;
  77.  
  78.   if(ParseExpression(tmp) == "" || reExpression != ENDSTR)
  79.   {
  80.     rePattern = "";
  81.     reExpression.Reset();
  82.     return NULL;
  83.   }
  84.   rePattern = tmp;
  85.   reExpression.Reset();
  86.   return rePattern;
  87. }
  88.  
  89. // ParseExpression() - Parse and translate an expression. Returns a pointer
  90. // to the compiled expression, or NULL on error.
  91.  
  92. const char* RegExp::ParseExpression(String& expr)
  93. {
  94.   String term;
  95.   expr = "";
  96.  
  97.   if(ParseTerm(term) == "")        // get the first term
  98.     return(NULL);
  99.  
  100.   while(reExpression == OR)        // parse all subsequent terms
  101.   {
  102.     expr += OR;
  103.     expr += term;
  104.     expr += END_TERM;
  105.     reExpression++;
  106.  
  107.     if(ParseTerm(term) == "")
  108.       return (NULL);
  109.   }
  110.   expr += term;
  111.   expr += END_TERM;
  112.  
  113.   return expr;
  114. }
  115.  
  116. // ParseTerm() - parse and translate a term.  Returns a pointer to the
  117. // compiled term or NULL on error.
  118.  
  119. const char* RegExp::ParseTerm(String& term)
  120. {
  121.   String factor;
  122.   term = "";
  123.  
  124.   if(reExpression == BOL)
  125.   {
  126.     term += reExpression[0];
  127.     reExpression++;
  128.   }
  129.   do
  130.   {
  131.     if(ParseFactor(factor) == "")
  132.       return(NULL);
  133.  
  134.     term += factor;
  135.   }
  136.   while(IsFactor());            // parse all factors of this term
  137.  
  138.   return term;
  139. }
  140.  
  141. // IsFactor() - returns TRUE for a valid factor character
  142.  
  143. int RegExp::IsFactor(void)
  144. {
  145.   static char* nfac_chars = "^|)]+?*";
  146.  
  147.   return (strchr(nfac_chars, reExpression[0]) == NULL) ? TRUE : FALSE;
  148. }
  149.  
  150. // ParseFactor() - parse and translate a factor.  Returns a pointer to the
  151. // compiled factor or NULL on error.
  152.  
  153. const char* RegExp::ParseFactor(String& factor)
  154. {
  155.   String tmp;
  156.   factor = "";
  157.  
  158.   switch(reExpression[0])
  159.   {
  160.     case LPAREN:            // ( - parenthesised expression
  161.       reExpression++;
  162.       ParseExpression(tmp);
  163.       factor += tmp;
  164.       if(reExpression != RPAREN)
  165.     return(NULL);
  166.       reExpression++;
  167.       break;
  168.  
  169.     case CCL:                // [ - character class; Ex: [0-9]
  170.       reExpression++;
  171.       ParseCCL(tmp);
  172.       factor += tmp;
  173.       if(reExpression != CCLEND)
  174.         return(NULL);
  175.       reExpression++;
  176.       break;
  177.  
  178.     case ANY:                // .
  179.     case EOL:                // $
  180.       factor += reExpression[0];
  181.       reExpression++;
  182.       break;
  183.  
  184.     case ESCAPE    :            // \
  185.       reExpression++;
  186.       factor += LITCHAR;
  187.       factor += ParseEscape();
  188.       reExpression++;
  189.       break;
  190.  
  191.     case CLOSURE:            // *
  192.     case POS_CLO:            // +
  193.     case ZERO_ONE:            // ?
  194.     case NEGATE:            // ^
  195.     case CCLEND:            // ]
  196.     case RPAREN:            // )
  197.     case OR:                // |
  198.       return(NULL);            // not valid characters
  199.  
  200.     default:                // literal character
  201.       factor += LITCHAR;
  202.       factor += reExpression[0];
  203.       reExpression++;
  204.       break;
  205.   }
  206.  
  207.   if(reExpression == CLOSURE  ||    // check for closure
  208.      reExpression == ZERO_ONE ||
  209.      reExpression == POS_CLO)
  210.   {
  211.     if(ParseClosure(factor) == FALSE)
  212.       return(NULL);
  213.   }
  214.   return factor;
  215. }
  216.  
  217. // ParseEscape() - returns ASCII value of character(s) following ESCAPE
  218.  
  219. char RegExp::ParseEscape(void)
  220. {
  221.   static unsigned char ch;
  222.  
  223.   switch(reExpression[0])
  224.   {
  225.     case 'b': reExpression++; return ('\b');    // backspace
  226.     case 't': reExpression++; return ('\t');    // tab
  227.     case 'f': reExpression++; return ('\f');    // formfeed
  228.     case 'n': reExpression++; return ('\n');    // linefeed
  229.     case 'r': reExpression++; return ('\r');    // carriage return
  230.  
  231.     case '0':                // 0-7 is octal constant
  232.     case '1':
  233.     case '2':
  234.     case '3':
  235.     case '4':
  236.     case '5':
  237.     case '6':
  238.     case '7':
  239.     {
  240.       ch = reExpression[0] - '0';
  241.       reExpression++;
  242.       if(reExpression >= '0' && reExpression < '8')
  243.       {
  244.         ch <<= 3;
  245.         ch += reExpression[0] - '0';
  246.         reExpression++;
  247.       }
  248.       if(reExpression >= '0' && reExpression < '8')
  249.       {
  250.         ch <<= 3;
  251.         ch += reExpression[0] - '0';
  252.         reExpression++;
  253.       }
  254.       return(ch);
  255.     }
  256.     default:                // otherwise, just that char
  257.       return(reExpression[0]);
  258.   }
  259. }
  260.  
  261. // ParseClosure() - place closure character and size before the factor
  262. // in the compiled string.
  263.  
  264. int RegExp::ParseClosure(String& factor)
  265. {
  266.   if(factor.Len() > 253)
  267.     return FALSE;            // closure expression too large
  268.  
  269.   factor.Insert(0, "  ");
  270.   factor[0] = reExpression[0];
  271.   factor[1] = (char)(factor.Len()-2);
  272.  
  273.   reExpression++;
  274.  
  275.   return TRUE;
  276. }
  277.  
  278. // ParseCCL() - parse and translate a character class. Return pointer to the
  279. // compiled class or NULL on error.
  280.  
  281. const char* RegExp::ParseCCL(String& ccl)
  282. {
  283.   int first = TRUE;
  284.   int len;
  285.  
  286.   ccl = "[ ";
  287.  
  288.   if(reExpression == NEGATE)        // if first character is NEGATE (^)
  289.   {
  290.     ccl[0] = NCCL;            // then we have a negated
  291.     reExpression++;            // character class
  292.   }
  293.  
  294.   // parse all characters up to the closing bracket or end of string marker
  295.  
  296.   while(reExpression != CCLEND && reExpression != ENDSTR)
  297.   {
  298.     if(reExpression == DASH && first == FALSE)    // DASH, check for range
  299.     {
  300.       reExpression++;
  301.       if(reExpression == NCCL)
  302.         ccl += DASH;            // not range, literal DASH
  303.       else
  304.       {
  305.         ParseDASH(ccl, reExpression);
  306.         reExpression++;
  307.       }
  308.     }
  309.     else
  310.     {
  311.       if(reExpression == ESCAPE)
  312.       {
  313.         reExpression++;
  314.         ccl += ParseEscape();
  315.         reExpression++;
  316.       }
  317.       else
  318.       {
  319.         ccl += reExpression[0];
  320.     reExpression++;
  321.       }
  322.     }
  323.     first = FALSE;
  324.   }
  325.  
  326.   len = ccl.Len()-2;
  327.  
  328.   if(len > 255)
  329.     return NULL;            // character class too large
  330.   else
  331.   {
  332.     ccl[1] = (char)len;            // store CCL length at ccl[1]
  333.     return ccl;
  334.   }
  335. }
  336.  
  337. // ParseDASH() - fill in range characters.
  338.  
  339. const char* RegExp::ParseDASH(String& ccl, char ch)
  340. {
  341.   static char c;
  342.  
  343.   for(c = ccl[ccl.Len() - 1] + 1; c <= ch; c++)
  344.     ccl += c;
  345.  
  346.   return ccl;
  347. }
  348.  
  349. /* -------------------------------------------------------------------- */
  350.  
  351. // Match() - Return the position of the first character of the left-most
  352. // longest substring of s that Matches pattern, or NULL if no match is
  353. // found. Sets RSTART and RLENGTH.
  354.  
  355. int RegExp::Match(const char* s, const char* pattern)
  356. {
  357.   char* c = (char*)s;
  358.   reLastChar = (char*)s;
  359.  
  360.   while(*c != ENDSTR)
  361.   {
  362.     if(MatchTerm(int(c-(char*)s), c, (char*)pattern) != FALSE)
  363.     {
  364.       RSTART  = int(c-(char*)s);
  365.       RLENGTH = int(reLastChar - c);
  366.       return RSTART;
  367.     }
  368.     c++;
  369.   }
  370.   RSTART = RLENGTH = 0;
  371.   return -1;
  372. }
  373.  
  374. // MatchTerm() - Match a compiled term. Returns TRUE, FALSE, or ALMOST.
  375.  
  376. int RegExp::MatchTerm(int offset, char* s, char* pattern)
  377. {
  378.   reLastChar = s;
  379.  
  380.   if(*pattern == ENDSTR)
  381.     return FALSE;
  382.  
  383.   do
  384.   {
  385.     switch(*pattern)
  386.     {
  387.       case BOL:                // ^ - match beginning of line
  388.         if(offset != 0)
  389.           return FALSE;
  390.         pattern++;
  391.         break;
  392.  
  393.       case LITCHAR:            // c - match literal character
  394.         if(*s++ != *++pattern)
  395.           return FALSE;
  396.         pattern++;
  397.         break;
  398.  
  399.       case END_TERM:            // e - skip end-of-term character
  400.         pattern++;
  401.         break;
  402.  
  403.       case ANY:                // . - match any character
  404.         if(*s++ == ENDSTR)        //     except end of string
  405.           return FALSE;
  406.         pattern++;
  407.         break;
  408.  
  409.       case OR:
  410.         return MatchOR(offset, s, pattern);
  411.  
  412.       case CCL:                // [ - character class
  413.       case NCCL:            // ]
  414.         if(*s == ENDSTR)
  415.           return FALSE;
  416.         if(!MatchCCL(*s++, pattern++))
  417.           return FALSE;
  418.         pattern += pattern[0] + 1;
  419.         break;
  420.  
  421.       case EOL:                // $ - match end of string
  422.         if(*s != ENDSTR)
  423.           return FALSE;
  424.         pattern++;
  425.         break;
  426.  
  427.       case ZERO_ONE:            // ?
  428.         return Match_0_1(offset, s, pattern);
  429.  
  430.       case CLOSURE:            // *
  431.       case POS_CLO:            // +
  432.       {
  433.         char ClosurePattern[1024];
  434.  
  435.         strncpy(ClosurePattern, pattern+2, *(pattern+1));
  436.         ClosurePattern[*(pattern+1)] = ENDSTR;
  437.         return MatchClosure(offset, s, pattern, ClosurePattern);
  438.       }
  439.  
  440.       default:
  441.  
  442.       // If we get to this point, then something has gone very wrong.
  443.       // Most likely, someone has tried to match with an invalid
  444.       // compiled pattern.
  445.  
  446.         return FALSE;
  447.     }
  448.     reLastChar = s;
  449.   } while(*pattern != ENDSTR);
  450.  
  451.   return TRUE;
  452. }
  453.  
  454. // MatchOR() - Handles selection processing.
  455.  
  456. int RegExp::MatchOR(int offset, char* s, char* pattern)
  457. {
  458.   char tmp_pattern[1024];
  459.   char *t1, *t2, *junk;
  460.  
  461.   // The first case is build into tmp_pattern. Second case is already there.
  462.   // Both patterns are searched to determine the longest matched substring.
  463.  
  464.   tmp_pattern[0] = ENDSTR;
  465.   pattern++;
  466.   junk = (char*)SkipTerm(pattern);
  467.  
  468.   strncat(tmp_pattern, pattern, int(junk-pattern));
  469.   strcat(tmp_pattern, SkipTerm(junk));
  470.   t1 = (MatchTerm(offset, s, tmp_pattern) != FALSE) ? reLastChar : NULL;
  471.  
  472.   // The second pattern need not be searched if the first pattern results
  473.   // in a match through to the end of the string, since the longest possible
  474.   // match has already been found.
  475.  
  476.   if(t1 == NULL || *reLastChar != ENDSTR)
  477.   {
  478.     t2 = (MatchTerm(offset, s, junk) != FALSE) ? reLastChar : NULL;
  479.  
  480.     // determine which matched the longest substring
  481.  
  482.     if(t1 != NULL && (t2 == NULL || t1 > t2))
  483.       reLastChar = t1;
  484.   }
  485.   return (t1 == NULL && t2 == NULL) ? FALSE : TRUE;
  486. }
  487.  
  488. // SkipTerm() - Skip over the current term and return a pointer to the
  489. // next term in the pattern.
  490.  
  491. const char* RegExp::SkipTerm(char* pattern)
  492. {
  493.   register int nterm = 1;
  494.  
  495.   while(nterm > 0)
  496.   {
  497.     switch(*pattern)
  498.     {
  499.       case OR:
  500.         nterm++;
  501.         break;
  502.  
  503.       case CCL:
  504.       case NCCL:
  505.       case CLOSURE:
  506.       case ZERO_ONE:
  507.       case POS_CLO:
  508.         pattern++;
  509.         pattern += pattern[0];
  510.         break;
  511.  
  512.       case END_TERM:
  513.         nterm--;
  514.         break;
  515.  
  516.       case LITCHAR:
  517.         pattern++;
  518.         break;
  519.     }
  520.     pattern++;
  521.   }
  522.   return pattern;
  523. }
  524.  
  525. // Match_0_1() - Match the ZERO_ONE operator. First, this routine attempts
  526. // to match the entire pattern with the input string. If that fails, it
  527. // skips over the closure pattern and attempts to match the rest of the
  528. // pattern.
  529.  
  530. int RegExp::Match_0_1(int offset, char* s, char* pattern)
  531. {
  532.   char* old_s = s;
  533.  
  534.   if(MatchTerm(offset, s, pattern+2) == TRUE)
  535.     return TRUE;
  536.   else if(MatchTerm(offset, old_s, pattern+2+*(pattern+1)) == FALSE)
  537.     return FALSE;
  538.   else
  539.     return ALMOST;
  540. }
  541.  
  542. // MatchClosure() - Match CLOSURE and POS_CLO. Match as many of the
  543. // closure patterns as possible, then attempt to match the remaining
  544. // pattern with what's left of the input string. Backtrack until we've
  545. // either matched the remaing pattern or we arrive back at where we
  546. // started.
  547.  
  548. int RegExp::MatchClosure(int offset, char* s,
  549.                          char* pattern, char* ClosurePattern)
  550. {
  551.   char* old_s = s;
  552.  
  553.   if(MatchTerm(offset, s, ClosurePattern) == TRUE)
  554.   {
  555.     old_s = reLastChar;
  556.     if(MatchClosure(offset, old_s, pattern, ClosurePattern) != FALSE)
  557.       return TRUE;
  558.     else
  559.       return MatchTerm(offset, old_s, pattern+2+*(pattern+1));
  560.   }
  561.   else if(*pattern != CLOSURE)      // POS_CLO requires at least one match
  562.     return FALSE;
  563.   else if(MatchTerm(offset, old_s, pattern+2+*(pattern+1)) == TRUE)
  564.     return ALMOST;
  565.   else
  566.     return FALSE;
  567. }
  568.  
  569. // MatchCCL() - Match a character class or negated character class
  570.  
  571. int RegExp::MatchCCL(char c, char* pattern)
  572. {
  573.   register int x;
  574.   char ccl = *pattern++;
  575.  
  576.   for(x = *pattern; x > 0; x--)
  577.   {
  578.     if(c == pattern[x])
  579.       return(ccl == CCL);
  580.   }
  581.   return(ccl != CCL);
  582. }
  583.  
  584. /* -------------------------------------------------------------------- */
  585.  
  586. // Return the position of the first instance of t in s, or -1 if no match.
  587.  
  588. int index(const String& s, const RegExp& t)
  589. {
  590.   return match(s, (RegExp)t);
  591. }
  592.  
  593. // sub() - Substitute 'to' for the leftmost longest substring of str
  594. // matched by the regular expression 'from'. Return number of substitutions
  595. // made.
  596.  
  597. int sub(RegExp& from, const String& to, String& str)
  598. {
  599.   if(match(str, from) == -1)
  600.     return 0;
  601.  
  602.   str.Replace(RSTART, RLENGTH, to);
  603.   return 1;
  604. }
  605.  
  606. // gsub() - Substitute 'to' globally for all substrings in str matched by
  607. // the regular expression 'from'. Return number of substitutions made.
  608.  
  609. int gsub(RegExp& from, const String& to, String& str, unsigned count)
  610. {
  611.   int n = 0;
  612.   ParseString tmp = str;
  613.  
  614.   while(match(tmp, from) != -1)
  615.   {
  616.     tmp.Replace(RSTART, RLENGTH, to);
  617.     tmp += RSTART+RLENGTH;
  618.     if(++n == count)
  619.       break;
  620.   }
  621.   tmp.Reset();
  622.   str = tmp;
  623.  
  624.   return n;
  625. }
  626.  
  627. // split() - split the string s into fields in the array a on field
  628. // separator fs. Returns number of fields.  Also sets the global variable
  629. // NF.
  630.  
  631. int split(const String& s, String*& array, const RegExp& fs)
  632. {
  633.   String *tmp;
  634.   ParseString ps = s;
  635.   int rstart  = RSTART;            // save RSTART and RLENGTH
  636.   int rlength = RLENGTH;
  637.   NF = 1;
  638.  
  639.   while(match(ps, (RegExp)fs) != -1)
  640.   {
  641.     ps += RSTART+RLENGTH;
  642.     NF++;
  643.   }
  644.  
  645.   tmp = new String[NF];
  646.   ps.Reset();
  647.   NF = 0;
  648.  
  649.   while(match(ps, (RegExp)fs) != -1)
  650.   {
  651.     tmp[NF++] = ps(0, RSTART);
  652.     ps += RSTART+RLENGTH;
  653.   }
  654.   array = tmp;
  655.  
  656.   RSTART  = rstart;            // restore globals
  657.   RLENGTH = rlength;
  658.   return (NF);
  659. }
  660.  
  661. ostream& operator<<(ostream& strm, const RegExp& re)
  662. {
  663.   return strm << re.reExpression();
  664. }
  665.  
  666.